function mxGraphHandler(graph)
{
	this.graph = graph;

	if (document.body != null)
	{
		this.graph.addMouseListener(this);
	}
};
mxGraphHandler.prototype.graph = null;
mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
mxGraphHandler.prototype.enabled = true;
mxGraphHandler.prototype.cloneEnabled = true;
mxGraphHandler.prototype.moveEnabled = true;
mxGraphHandler.prototype.selectEnabled = true;
mxGraphHandler.prototype.removeCellsFromParent = true;
mxGraphHandler.prototype.connectOnDrop = false;
mxGraphHandler.prototype.scrollOnMove = true;
mxGraphHandler.prototype.minimumSize = 6;
mxGraphHandler.prototype.previewColor = 'black';
mxGraphHandler.prototype.shape = null;
mxGraphHandler.prototype.isEnabled = function()
{
	return this.enabled;
};
mxGraphHandler.prototype.setEnabled = function(value)
{
	this.enabled = value;
};
mxGraphHandler.prototype.isCloneEnabled = function()
{
	return this.cloneEnabled;
};
mxGraphHandler.prototype.setCloneEnabled = function(value)
{
	this.cloneEnabled = value;
};
mxGraphHandler.prototype.isMoveEnabled = function()
{
	return this.moveEnabled;
};
mxGraphHandler.prototype.setMoveEnabled = function(value)
{
	this.moveEnabled = value;
};
mxGraphHandler.prototype.isSelectEnabled = function()
{
	return this.selectEnabled;
};
mxGraphHandler.prototype.setSelectEnabled = function(value)
{
	this.selectEnabled = value;
};
mxGraphHandler.prototype.isRemoveCellsFromParent = function()
{
	return this.removeCellsFromParent;
};
mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
{
	this.removeCellsFromParent = value;
};
mxGraphHandler.prototype.mouseDown = function(sender, me)
{
	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled()
		&& !this.graph.isForceMarqueeEvent(me.getEvent()) && me.getHandle() == null && me.getState() != null)
	{
		var cell = me.getCell();
		this.cell = null;
		this.delayedSelection = this.graph.isCellSelected(cell);

		if (this.isSelectEnabled() && !this.delayedSelection)
		{
			this.graph.selectCellForEvent(cell, me.getEvent());
		}

		if (this.isMoveEnabled())
		{
			var model = this.graph.model;
			var geo = model.getGeometry(cell);

			if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1
				|| (geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null
				|| model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges
				|| (this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
			{
				this.cell = cell;
				this.start(me.getX(), me.getY());
			}
			this.cellWasClicked = true;
			me.consume();
		}
	}
};
mxGraphHandler.prototype.getCells = function(initialCell)
{
	return this.graph.getMovableCells(this.graph.getSelectionCells());
};
mxGraphHandler.prototype.getPreviewBounds = function(cells)
{
	var bounds = this.graph.getView().getBounds(cells);

	if (bounds != null)
	{
		if (bounds.width < this.minimumSize)
		{
			var dx = this.minimumSize - bounds.width;
			bounds.x -= dx / 2;
			bounds.width = this.minimumSize;
		}

		if (bounds.height < this.minimumSize)
		{
			var dy = this.minimumSize - bounds.height;
			bounds.y -= dy / 2;
			bounds.height = this.minimumSize;
		}
	}
	return bounds;
};
mxGraphHandler.prototype.createPreviewShape = function(bounds)
{
	var shape = new mxRectangleShape(bounds, null, this.previewColor);
	shape.isDashed = true;
	return shape;
};
mxGraphHandler.prototype.start = function(x, y)
{
	var pt = mxUtils.convertPoint(this.graph.container, x, y);
	this.startX = pt.x;
	this.startY = pt.y;
	this.cells = this.getCells(this.cell);
	this.bounds = this.getPreviewBounds(this.cells);

	if (this.bounds != null)
	{
		this.shape = this.createPreviewShape(this.bounds);

		if (mxClient.IS_GC || mxClient.IS_SF || mxClient.IS_OP)
		{
			this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
			this.shape.init(this.graph.container);
		}
		else
		{

			this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG)
				? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
			this.shape.init(this.graph.getView().getOverlayPane());
		}

		if (this.shape.dialect == mxConstants.DIALECT_SVG)
		{
			this.shape.node.setAttribute('style', 'pointer-events:none;');
		}
		else
			(this.shape.dialect == mxConstants.DIALECT_VML)

		{
			var self = this;
		mxEvent.addListener(this.shape.node, 'mousemove', function(evt)
		{
			var state = self.graph.getView().getState(self.target || self.cell);
			self.graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, state));
		});
		}
		this.shape.node.style.visibility = 'hidden';
		this.highlight = new mxCellHighlight(this.graph, mxConstants.DROP_TARGET_COLOR, 3);
	}
};
mxGraphHandler.prototype.mouseMove = function(sender, me)
{
	if (!me.isConsumed() && this.cell != null && this.shape != null && this.shape.node != null)
	{
		var graph = this.graph;
		var point = mxUtils.convertPoint(graph.container, me.getX(), me.getY());
		var dx = point.x - this.startX;
		var dy = point.y - this.startY;
		var tol = graph.tolerance;

		if (this.shape.node.style.visibility == 'visible' || Math.abs(dx) > tol || Math.abs(dy) > tol)
		{
			var trx = graph.getView().translate;
			var scale = graph.getView().scale;

			if (graph.isGridEnabledEvent(me.getEvent()))
			{
				var tx = this.bounds.x - (this.graph.snap(this.bounds.x / scale - trx.x) + trx.x) * scale;
				var ty = this.bounds.y - (this.graph.snap(this.bounds.y / scale - trx.y) + trx.y) * scale;
				dx = this.graph.snap(dx / scale) * scale - tx;
				dy = this.graph.snap(dy / scale) * scale - ty;
			}

			if (graph.isConstrainedEvent(me.getEvent()))
			{
				if (Math.abs(dx) > Math.abs(dy))
				{
					dy = 0;
				}
				else
				{
					dx = 0;
				}
			}
			this.shape.bounds = new mxRectangle(this.bounds.x + dx, this.bounds.y + dy, this.bounds.width,
				this.bounds.height);
			this.shape.node.style.visibility = 'visible';
			this.shape.redraw();
			var target = null;
			var cell = me.getCell();

			if (this.shape != null && this.shape.dialect == mxConstants.DIALECT_STRICTHTML)
			{
				cell = graph.getCellAt(point.x, point.y)
			}

			if (graph.isDropEnabled())
			{
				target = graph.getDropTarget(this.cells, me.getEvent(), cell);
			}
			var parent = target;
			var model = graph.getModel();

			while (parent != null && parent != this.cell)
			{
				parent = model.getParent(parent);
			}
			var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
			var state = graph.getView().getState(target);
			var highlight = false;

			if (!graph.isCellSelected(target) && state != null && parent == null
				&& (model.getParent(this.cell) != target || clone))
			{
				if (this.target != target)
				{
					this.target = target;
					this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
				}
				highlight = true;
			}
			else
			{
				this.target = null;

				if (this.connectOnDrop && cell != null && this.cells.length == 1 && graph.getModel().isVertex(cell)
					&& graph.isCellConnectable(cell))
				{
					var state = graph.getView().getState(cell);

					if (state != null)
					{
						var error = graph.getEdgeValidationError(null, this.cell, cell);
						var color = (error == null)
							? mxConstants.VALID_COLOR : mxConstants.INVALID_CONNECT_TARGET_COLOR;
						this.setHighlightColor(color);
						highlight = true;
					}
				}
			}

			if (state != null && highlight)
			{
				this.highlight.highlight(state);
			}
			else
			{
				this.highlight.hide();
			}
		}
		me.consume();
	}
};
mxGraphHandler.prototype.setHighlightColor = function(color)
{
	if (this.highlight != null)
	{
		this.highlight.setHighlightColor(color);
	}
};
mxGraphHandler.prototype.mouseUp = function(sender, me)
{
	if (!me.isConsumed())
	{
		var graph = this.graph;

		if (this.cell != null && this.shape != null && this.shape.node.style.visibility == 'visible')
		{
			var point = mxUtils.convertPoint(graph.container, me.getX(), me.getY());
			var trx = graph.getView().translate;
			var scale = graph.getView().scale;
			var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
			var dx = (point.x - this.startX) / scale;
			var dy = (point.y - this.startY) / scale;

			if (graph.isGridEnabledEvent(me.getEvent()))
			{
				var tx = this.bounds.x - (graph.snap(this.bounds.x / scale - trx.x) + trx.x) * scale;
				var ty = this.bounds.y - (graph.snap(this.bounds.y / scale - trx.y) + trx.y) * scale;
				dx = graph.snap(dx) - tx / scale;
				dy = graph.snap(dy) - ty / scale;
			}

			if (graph.isConstrainedEvent(me.getEvent()))
			{
				if (Math.abs(dx) > Math.abs(dy))
				{
					dy = 0;
				}
				else
				{
					dx = 0;
				}
			}
			var cell = me.getCell();

			if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell)
				&& graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
			{
				graph.connectionHandler.connect(this.cell, cell, me.getEvent());
			}
			else
			{
				var cells = graph.getSelectionCells();
				var target = this.target;

				if (graph.isSplitEnabled() && graph.isSplitTarget(target, cells, me.getEvent()))
				{
					graph.splitEdge(target, cells, null, dx, dy);
				}
				else
				{
					this.moveCells(graph.getSelectionCells(), dx, dy, clone, this.target, me.getEvent());
				}
			}
		}
		else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
		{
			graph.selectCellForEvent(this.cell, me.getEvent());
		}
	}

	if (this.cellWasClicked)
	{
		me.consume();
	}
	this.reset();
};
mxGraphHandler.prototype.reset = function()
{
	this.destroyShapes();
	this.cellWasClicked = false;
	this.delayedSelection = false;
	this.cell = null;
	this.target = null;
};
mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
{
	if (this.graph.getModel().isVertex(parent))
	{
		var pState = this.graph.getView().getState(parent);
		var pt = mxUtils.convertPoint(this.graph.container, evt.clientX, evt.clientY);
		return pState != null && !mxUtils.contains(pState, pt.x, pt.y);
	}
	return false;
};
mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
{
	if (clone)
	{
		cells = this.graph.getCloneableCells(cells);
	}

	if (target == null && this.isRemoveCellsFromParent()
		&& this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell), cells, evt))
	{
		target = this.graph.getDefaultParent();
	}

	var cells = this.graph.moveCells(cells, dx, dy, clone, target, evt);

	if (this.isSelectEnabled() && this.scrollOnMove)
	{
		this.graph.scrollCellToVisible(cells[0]);
	}

	if (clone)
	{
		this.graph.setSelectionCells(cells);
	}
};
mxGraphHandler.prototype.destroyShapes = function()
{
	if (this.shape != null)
	{
		this.shape.destroy();
		this.shape = null;
	}

	if (this.highlight != null)
	{
		this.highlight.destroy();
		this.highlight = null;
	}
};
mxGraphHandler.prototype.destroy = function()
{
	this.graph.removeMouseListener(this);
	this.destroyShapes();
};